#pragma once

#include <common.h>
#include <ir/ir.h>
#include <ir/block.h>
#include <ir/inst.h>
#include <ir/global_reg.h>
#include <riscv/genarator.h>
#include <riscv/stack.h>
#include <riscv/global.h>
#include <riscv/func_stack.h>

namespace ir {
  class CompUnit;
  class Func;
  class Block;

  class InstImm;
  class InstAlloc;
  class InstStore;
  class InstLoad;
  class InstArrayAlloc;
  class InstArrayStore;
  class InstArrayLoad;
  class InstGlobalAlloc;
  class InstGlobalStore;
  class InstGlobalLoad;
  class InstI32ToF32;
  class InstF32ToI32;
  class InstAdd;
  class InstSub;
  class InstMul;
  class InstDiv;
  class InstJmp;
  class InstBne;
  class InstBeq;
  class InstCall;
  class InstRet;
}

namespace riscv {
  class Visitor;

  class Visitor {
  public:

    Visitor(CompUnit * comp_unit);

    void visit(ir::CompUnit & comp_unit);
    void visit(ir::Func & func);
    void visit(ir::Block & block);
    void visit(ir::InstImm & inst_imm);
    void visit(ir::InstAlloc & inst_alloc);
    void visit(ir::InstStore & inst_store);
    void visit(ir::InstLoad & inst_load);
    void visit(ir::InstArrayAlloc & inst_array_alloc);
    void visit(ir::InstArrayStore & inst_array_store);
    void visit(ir::InstArrayLoad & inst_array_load);
    void visit(ir::InstGlobalAlloc & inst_global_alloc);
    void visit(ir::InstGlobalStore & inst_global_store);
    void visit(ir::InstGlobalLoad & inst_global_load);
    void visit(ir::InstI32ToF32 & inst_i32_to_f32);
    void visit(ir::InstF32ToI32 & inst_f32_to_i32);
    void visit(ir::InstAdd & inst_add);
    void visit(ir::InstSub & inst_sub);
    void visit(ir::InstMul & inst_mul);
    void visit(ir::InstDiv & inst_div);
    void visit(ir::InstJmp & inst_jmp);
    void visit(ir::InstBne & inst_bne);
    void visit(ir::InstBeq & inst_beq);
    void visit(ir::InstCall & inst_call);
    void visit(ir::InstRet & inst_ret);

  private:
    CompUnit * comp_unit;
    std::unique_ptr<Genarator> genarator;
    Func * current_func;
    Block * current_block;
    // unsigned int stack_size;
    std::map<ir::Reg *, Stack> ir_reg_to_stack;
    std::map<ir::Reg *, Reg> ir_reg_to_reg;
    std::map<ir::Reg*, FReg> ir_reg_to_freg;
    std::map<ir::Block *, Block*> ir_block_to_block;
    std::map<ir::GlobalReg *, Global *> ir_global_reg_to_global;
    std::map<ir::Func *, Func *> ir_func_to_func;
    std::map<Func *, FuncStack> func_to_func_stack;

    void use_ir_reg(ir::Reg * ir_reg);
    Reg temp_trans_stored_reg(Reg temp_reg);
  };
}
